答疑大杂烩
# 答疑大杂烩
[TOC]
# 1. 一次性插入1000个div,如何优化插入的性能
使用Fragment
var fragment = document.createDocumentFragment(); fragment.appendChild(elem);
1
2
向1000个并排的div元素中,插入一个平级的div元素,如何优化插入的性能
- 先display:none 然后插入 再display:block
- 赋予key,然后使用virtual-dom,先render,然后diff,最后patch
- 脱离文档流,用GPU去渲染,开启硬件加速
# 2.替换和不可替换元素
- 替换元素
替换元素就是浏览器根据元素的标签和属性,来决定元素的具体显示内容。
例如浏览器会根据<img>
标签的 src 属性的值来读取图片信息并显示出来,而如果查看 HTML 代码,则看不到图片的实际内容;又例如根据 <input>
标签的 type 属性来决定是显示输入框,还是单选按钮等。
HTML 中的<img> 、<input>、<textarea> 、<select> 、<object>
都是替换元素。这些元素往往没有实际的内容,即是一个空元素。
浏览器会根据元素的标签类型和属性来显示这些元素。可替换元素也在其显示中生成了框。
- 不可替换元素
HTML 的大多数元素是不可替换元素,即其内容直接表现。
# 3.不能作为判断条件
- 相对路径(绝对路径才可以)
- 颜色值
- 背景
# 4.中文文字下用word-spacing没有效果?
letter-spacing
为每个字符之间的空白距离,word-spacing
为单词之间的空白距离,故应使用letter-spacing
。
# 5.a
标签中的href="javascript:;"
是什么意思?
- javascript: 是一个伪协议。javascript:是表示在触发a标签默认动作时,执行一段Js代码,而javascript:;表示什么都不执行,就是去掉a标签的默认行为(防止页面刷新和跳转),这样点击a标签时就没有任何反应。
- 跟
href="javascript:void(0)"
是一样的,void是Js的一个运算符,void(0)就是什么都不做的意思。 - 有了href才支持回车点击和默认可以获取焦点
# 6.overflow:hidden
可以实现什么效果?
- 父元素无height值:清除别人浮动对自己的影响;子元素浮动时,则在父元素使用
overflow: hidden
可以清除浮动,使得父元素的高度依旧是靠子元素撑开. - 父元素有height值:隐藏子元素超出父元素的部分。
# 7.使用clear:both
要注意什么问题?
使用后margin属性失效。
clear
的定义是:元素盒子的边不能与前面的浮动元素相邻。也就是虽然浮动元素高度坍塌,但是设置了clear: both
的元素却将其高度视为仍然占据位置。
clear
只能作用于块级元素,并且其并不能解决后面元素可能发生的文字环绕问题。
# 8.NPM依赖包版本号
~
会匹配1.2.X版本的最新版^
会匹配^1.x.x版本的最新版*
匹配最最新版
# 9. CSS3硬件加速
使用css3硬件加速,可以让transform、opacity、filter这些动画不会引起回流重绘。(只有transform、opacity、filter属性变化会直接在GPU处理)
对于动画的其他属性,比如background-color,还是会引起回流重绘的,不过它还是可以提升这些动画的性能。
常见的触发硬件加速的css属性:transform、opacity、filters、will-change;HTML5标签:video、canvas、webgl。
若太多元素用CSS3硬件加速,会导致内存占用较大,存在性能问题。(GPU处理过多的内容会导致内存问题,这在移动端和移动端浏览器会导致崩溃)
如果不在动画结束的时候关闭硬件加速,会产生字体模糊。(因为GPU和CPU的算法不同,因此在动画结束后要关闭硬件加速)
会引发耗电量增加。
性能优化
/* 告诉浏览器该属性会发生变化,因此浏览器会在开始之前对其进行优化 */ .example { will-change: transform; }
1
2
3
4
# 10.
- 以下原因可能导致不能自动播放: chrome6以及更高的版本不允许媒体自动播放; safari 阻止自动播放视频。opera 阻止autoplay; 出于用户体验,节省流量的考虑,移动端禁止自动播放; 视频文件太大,加载时间过长或错误。
- 解决办法:视频元素添加muted 属性,如
<video controls muted>
。
# 11.
# 12.position:sticky
失效原因
参考链接:https://juejin.cn/post/6909393635557507086#heading-5
可能的原因有:
1. 包裹的父级容器高度与sticky元素一致。
2. 包裹的父容器设置了`overflow:hidden`。
# 13.float
与display: inline-block
的使用
- 一般情况下,使用
float
。float
就是隐性的把内联元素转化为块元素,近似于display:inline-block
。为什么不直接display:inline-block
,因为显示会有几个px
的bug。 float
需要清除浮动;display: inline-block
则不需要。display: inline-block
默认vertical-align: bottom
,可以通过vertical
属性设置默认基线;float
则不行。
# 14. input为什么多出1px?
- 参考链接:https://segmentfault.com/q/1010000005367537
# 15. 一个空格的大小
- 字体样式不一样,空格大小也不一样。
- 只有当前字体为宋体时,空格大小为字体大小的一半。
# 16.opacity:0
、display: none
与visibility:hidden
的区别
结构
display:none: 会让元素完全从渲染树中消失,不占空间, 不能点击。 visibility: hidden和opacity: 0:不会让元素从渲染树消失,仍占空间。但visibility: hidden的元素不可点击而opacity: 0可以点击。
继承 display: none:非继承属性,子孙节点由于祖先元素从渲染树消失而无法显示。 visibility: hidden:继承属性,子孙节点消失由于继承了hidden,通过设置visibility: visible;可以让子孙节点显示。
opacity:0:继承属性,但子元素并不能通过
opacity: 1
来取消隐藏。性能 display: none:修改元素会引起回流,性能消耗较大。 visibility:hidden和opacity: 0 : 修改元素会造成重绘,性能消耗较少。
# 17.["1","2","3"].map(parseInt)
- 结果: [1, NaN, NaN]
- parseInt()接收两个参数,第二个参数是进制数。
- map()在调用callback函数时,会给它传递三个参数(val,index,arr):当前正在遍历的元素,元素索引,原数组本身。
- 第三个参数parseInt()会无视,第二个索引参数则作为进制数处理。
# 18.字符串不可变,此话怎讲?
当我们想要改变某个变量保存的字符串:它先是销毁了原来的字符串,再用另一个包含新值的字符串填充该变量。
var lang = "Java";
lang = lang + "Script";
2
- 第一步:创建一个可以容纳10个字符的新字符串。
- 第二步:在这个字符串中填充"Java"和"Script"
- 第三步:销毁原来的字符串"Java"和"Script",因为已经用不到了,我们需要的是第一步时候创建的新字符串。
# 19.块元素与内联元素嵌套规则
- 块元素
div , p , form, ul, li , ol, dl, form, address, hr, menu, table
- 行内元素
span, strong, em, br,label, select, textarea, cite
- 行内块
- img、input、td
<a>
实际能嵌套块元素(原则上不能)。- 有几个特殊的块级元素只能包含内联元素,不能再包含块级元素。这几个特殊标签是:
h1
、h2
、h3
、h4
、h5
、h6
、p
、dt
。 li
可以包含div
,但不可以包含li
。
# 20.ECMAScript 自动分号;
插入问题
- 虽然js不强制使用分号,但实际上它需要分号来解析源代码。
- js解析器在遇到由于缺少分号导致的解析错误,会自动在源代码插入分号。
- 为了避免解析器猜测错误而导致错误处理,则需要了解插入分号的机制和原理。
- “必须用分号终止某些 ECMAScript 语句 ( 空语句 , 变量声明语句 , 表达式语句 , do-while 语句 , continue 语句 , break 语句 , return 语句 ,throw 语句 )。这些分号总是明确的显示在源文本里。然而,为了方便起见,某些情况下这些分号可以在源文本里省略。描述这种情况会说:这种情况下给源代码的 token 流自动插入分号。”
- 建议绝对不要省略分号,同时也提倡将花括号和相应的表达式放在一行, 对于只有一行代码的 if 或者 else 表达式,也不应该省略花括号。 这些良好的编程习惯不仅可以提到代码的一致性,而且可以防止解析器改变代码行为的错误处理。
# 21.为什么css放头部,js放尾部?
- CSS 资源阻塞渲染。
- 构建Render树需要DOM和CSSOM,所以HTML和CSS都会阻塞渲染。所以需要让CSS尽早加载(如:放在头部),以缩短首次渲染的时间。
- JS 资源阻塞浏览器的解析。
- 发现一个外链脚本时,需等待脚本下载完成并执行后才会继续解析HTML。
- 浏览器中js引擎线程和渲染线程是互斥的,详见《从setTimeout-setInterval看JS线程》 (opens new window)。普通的脚本会阻塞浏览器解析,加上defer或async属性,脚本就变成异步,可等到解析完毕再执行。
- async异步执行,异步下载完毕后就会执行,不确保执行顺序,一定在onload前,但不确定在DOMContentLoaded事件的前后。
- defer延迟执行,相对于放在body最后(理论上在DOMContentLoaded事件前)。
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link href="style.css" rel="stylesheet">
</head>
<body>
<p>Hello <span>web performance</span> students!</p>
<div><img src="awesome-photo.jpg"></div>
<script src="app.js"></script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
- 浏览器拿到HTML后,从上到下顺序解析文档
- 此时遇到css、js外链,则同时发起请求
- 开始构建DOM树
- 这里要特别注意,由于有CSS资源,CSSOM还未构建前,会阻塞js(如果有的话)
- 无论JavaScript是内联还是外链,只要浏览器遇到 script 标记,唤醒 JavaScript解析器,就会进行暂停 blocked 浏览器解析HTML,并等到 CSSOM 构建完毕,才执行js脚本
- 渲染首屏(DOMContentLoaded 触发,其实不一定是首屏,可能在js脚本执行前DOM树和CSSOM已经构建完render树,已经paint)
# 22. ol标签出现不显示序号的情况
- 导致的原因可能是
*{margin:0;padding:0;}
- 方法一:
ol{padding-left: 20px;}
- 方法二:
ol li{list-style-type:decimal;list-style-position:inside;}
# 23.QuerySelector
/QuerySelectorAll
和getElementById
/getElementsByClassName
的区别
兼容性:通过https://caniuse.com/比较,没区别。
效率:测试发现GEBI和GEBC比QS和QSA要快
console.time('querySelector'); for (var i = 0; i < 100000; i++) { document.querySelector("#test"); } console.timeEnd('querySelector');
1
2
3
4
5灵活性:QS/QSA 均支持CSS的选择器(但不支持伪类选择器,更灵活。
querySelector('div img .test') //找到div下面的img下面类名为test的元素
1
2
4.动态性:通过QSA选择的不受后来DOM变化的影响,但是通过GEBC会受DOM的影响。
a = document.querySelectorAll('img');
b =document.getElementsByTagName('img');
document.body.appendChild(new Image());
console.log(a.length); // 0
console.log(b.length); // 1
2
3
4
5
# 24. 结束循环和遍历
- 数组遍历(break, continue均不合法)
- forEach:无法结束遍历,只能用return退出本次回调,进行下一次回调。
- filter:同上。
- map:同上。
- reduce,reduceRight:同上。
- every:只要有元素不满足则返回false,并结束遍历。
- some:只要有元素满足则返回true,并结束遍历。
- find、findIndex:找到就停止遍历。
- keys、values、entries:不接受传参。
- for循环
- for:可break,continue,不可return。
- for...in:同上。
- for...of:同上。
- while循环:可break,continue,不可return。
# 25.
# 26.
# 27.
# 28.(function(window,document,undefined){})(window,document);
的意义?
- window, document实参分别接受window, document对象,window, document对象都是全局环境下的,而在函数体内的window, document其实是局部变量,不是全局的。
- **可以提高性能,减少作用域链的查询时间。**如果你在函数体内需要多次调用window 或 document对象,这样把window 或 document对象当作参数传进去,这样做是非常有必要的。当然如果你的插件用不到这两个对象,那么就不用传递这两个参数了。
- undefined在老一辈的浏览器是不被支持的,直接使用会报错,JS框架要考虑到兼容性,因此 增加一个形参undefined。
# 29.从URL输入到页面展示,到底发生了什么?
首先通过DNS将URL解析为对应的IP地址;(DNS缓存,DNS prefetch)
然后与IP地址确定的服务器建立TCP连接; (长连接,预连接,接入SPDY协议)
随后向服务器抛出HTTP请求; (减少请求次数,减小请求体积,CDN)
服务端处理请求并发送数据给回客户端;
浏览器解析渲染页面.(资源加载优化,服务端渲染,浏览器缓存机制的利用,DOM树的构建,网页排版和渲染过程,回流与重绘的考量,DOM操作的合理规避)
从HTTP请求回来,就产生了流式的数据,后续的DOM树构建、CSS计算、渲染、合成、绘制,都是尽可能地流式处理前一步的产出:即不需要等到上一步骤完全结束,就开始处理上一步的输出,这样我们在浏览网页时,才会看到逐步出现的页面。
DNS解析
- 将域名解析成IP地址
TCP连接
- 三次握手
发送HTTP请求
服务器处理请求并返回HTTP报文
浏览器解析渲染页面
断开连接
- 四次挥手
# 30.服务端渲染SSR
- 服务端渲染本质上是把本该浏览器做的事情,分给服务器去做。
- 可以用来优化首屏渲染体验和SEO。
- 但由于服务器稀少而珍贵,所以应该优先考虑其他方案来解决。
# 31.有哪些值是假?
- 假:0、NaN、''、false、undefined、null(六个假)
// 使用Boolean构造函数过滤数组中的所有假值
const compact = arr => arr.filter(Boolean);
compact([0, NaN, false, '', undefined, null, 'a', 1, [], {}," "]); // ["a", 1, [], {}, " "]
2
3
# 32.避免在不必要的情况下使用eval
eval() 是一个危险的函数, 他执行的代码拥有着执行者的权利。
如果你用 eval() 运行的字符串代码被恶意方(不怀好意的人)操控修改,您最终可能会在您的网页/扩展程序的权限下,在用户计算机上运行恶意代码。
更重要的是
,第三方代码可以看到某一个eval()被调用时的作用域,这也有可能导致一些不同方式的攻击。
相似的 Function 就不容易被攻击。
eval() 通常比替代方法慢,因为它必须调用 JS 解释器,而许多其他结构则由现代 JS 引擎进行优化。
在常见的案例中我们都会找更安全或者更快的方案去替换 eval()。
# 33.
# 34.HTTP请求中 request payload 和 formData 区别?
FormData和Payload是浏览器传输给接口的两种格式,这两种方式浏览器是通过Content-Type来进行区分的(了解Content-Type (opens new window)),如果是 application/x-www-form-urlencoded的话,则为formdata方式,如果是application/json或multipart/form-data的话,则为 request payload的方式。
# 35. 怎么判断一个值是不是NaN?
- 结论
- ES6新增的
Number.isNaN(NaN)
Object.is(NaN, NaN)
function valueIsNaN(v) { return v !== v; }
- ES6新增的
typeof NaN // “number”
- 不能用等号运算符来判断。
NaN === NaN // false
NaN == NaN // false
Number.NaN === NaN; // false
2
3
- isNaN只要不是number就会返回true。
isNaN(NaN); // true
isNaN(Number.NaN); // true
isNaN('A String'); // true
isNaN(undefined); // true
isNaN({}); // true
2
3
4
5
- ES6新增Number.isNaN(),只有NaN才返回true。
Number.isNaN(NaN); // true
Number.isNaN('A String'); // false
Number.isNaN(undefined); // false
Number.isNaN({}); // false
2
3
4
- 数字中只有NaN不等于自身。
function valueIsNaN(v) { return v !== v; }
valueIsNaN(1); // false
valueIsNaN(NaN); // true
valueIsNaN(Number.NaN); // true
// 以下比较运算符两边是两个不同的实例
[] !== [] // true
{} !== {} // true
// 以下则比较的是同一个实例
valueIsNaN([]) // false
valueIsNaN({}) // false
2
3
4
5
6
7
8
9
10
11
- ES6新增,与严格相等运算符行为基本一致,除了以下两种。
Object.is(NaN, NaN); // true
Object.is(-0, +0); // false
NaN === NaN // false
-0 === +0 // true
2
3
4
# 36. 良好的CSS选择器书写习惯
因为CSS选择符是从右到左进行匹配的,比如
#id li
是先遍历页面所有的li
元素,再去确认li
的父级是不是#id
。因此在书写CSS选择器的时候,应该尽可能让浏览器少遍历元素。
- 避免使用通配符,通配符会导致浏览器需要遍历每一个元素。
- 少用标签选择器。
- 减少嵌套。
# 37. 在网页中的应该使用奇数还是偶数的字体?
使用偶数字体。偶数字号相对更容易和 web 设计的其他部分构成比例关系。Windows 自带的点阵宋体(中易宋体)从 Vista 开始只提供 12、14、16 px 这三个大小的点阵,而 13、15、17 px时用的是小一号的点。(即每个字占的空间大了 1 px,但点阵没变),于是略显稀疏。
# 38. 数据类型的判断
- typeof 返回一个表示数据类型的字符串,返回结果包括:number、boolean、string、symbol、object、undefined、function7种数据类型,但不能判断null、array等。
- instanceof 是用来判断A是否为B的实例,表达式为:A instanceof B,如果A是B的实例,则返回true,否则返回false。instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性,但它不能检测null 和 undefined(没有prototype属性)。
- Object.prototype.toString.call() 是最准确最常用的方式。
- 每个对象都有一个
toString()
方法,当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用。默认情况下,toString()
方法被每个Object
对象继承。如果此方法在自定义对象中未被覆盖,toString()
返回 "[object type]",其中type
是对象的类型。 - type类型有:String、Number、Boolean、Undefined、Null、Function、Date、Array、RegExp、Error。
- 每个对象都有一个
# 39. 怎么处理移动端 1px 被渲染成 2px 的问题?
手机分表率很高的时候,比如二倍屏就会显示2px。
- width:设置viewport宽度,为一个正整数,或为device-width(设备宽度)。
- height:设置viewport高度,一般设置了宽度,会自动解析出高度,可以不用设置。
- initial-scale:默认缩放比例(初始缩放比例),为一个数字,可以带小数。
- minimum-scale:允许用户最小缩放比例,为一个数字,可以带小数。
- maximum-scale:允许用户最大缩放比例,为一个数字,可以带小数。
- user-scalable:是否允许手动缩放。
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />
局部处理
meta标签中的viewport属性,initial-scale设置为1。
rem按照设计稿走,然后利用transform的scale(0.5)缩小一倍即可。
全局处理
meta标签中的viewport属性,initial-scale设置为0.5。
rem按照设计稿走即可。
# 40. z-index失效
- z-index属性只作用在被定位了的元素上。所以如果你在一个没被定位的元素上使用z-index的话,是不会有效果的。
- 同一个父元素下的元素的层叠效果会受父元素的z-index影响,如果父元素的z-index值很小,那么子元素的z-index值很大也不起作用。
# 41. 获取路径
//js获取项目根路径,如: http://localhost:8083/uimcardprj
function getRootPath(){
//获取当前网址,如: http://localhost:8083/uimcardprj/share/meun.jsp
var curWwwPath=window.document.location.href;
//获取主机地址之后的目录,如: uimcardprj/share/meun.jsp
var pathName=window.document.location.pathname;
var pos=curWwwPath.indexOf(pathName);
//获取主机地址,如: http://localhost:8083
var localhostPaht=curWwwPath.substring(0,pos);
//获取带"/"的项目名,如:/uimcardprj
var projectName=pathName.substring(0,pathName.substr(1).indexOf('/')+1);
return(localhostPaht+projectName);
}
2
3
4
5
6
7
8
9
10
11
12
13
# 42. Object.create(null)
与{}
的区别?
参考链接:详解Object.create(null) (opens new window)
Object.create()
的第一个参数是指新创建对象的原型对象,以null作为原型,可以避免继承了Object
自身的方法,如hasOwnProperty
、toString
等。{}
相当于Object.create(Object.prototype)
。- 需要一个非常干净且高度可定制的对象当作数据字典的时候;或想节省
hasOwnProperty
带来的一丢丢性能损失并且可以偷懒少些一点代码的时候,就可以用Object.create(null)
。
# 43. DOM操作为什么是昂贵的?
- JS引擎和渲染引擎(浏览器内核)是独立实现的。当用JS操作DOM时,本质上是JS引擎和渲染引擎进行了跨界交流。
- 对DOM修改时若引发样式改变,就会触发回流或重绘。
- 减少DOM操作的核心思路,就是让JS给DOM分压。
# 44. 获取文本内容的方法
element.innerHTML
会直接返回element节点下所有的HTML化的文本内容,只能作用于元素节点调用;文本节点并不能使用这个方法,会返回undefined。nodeValue
<span class="name">lhj</span>
// 属性节点的 nodeValue属性返回属性值
$('.name')[0].firstChild.nodeValue // lhj
// 元素节点的 nodeValue属性返回null
$('.name')[0].nodeValue // null
2
3
4
textContent
类似innerHTML
,在DOM中标签换行产生的空白字符会计入DOM中作为文本节点。innerText
不常用。